// fileio.cpp --
// $Id: fileio.cpp,v 1.26 2005/11/12 00:07:26 jcw Exp $
// This is part of Metakit, see http://www.equi4.com/metakit/

/** @file
 * Implementation of c4_FileStrategy and c4_FileStream
 */

#include <bautils.h>
#include "header.h"

#include "mk4io.h"
#include "myutil.h"


#ifndef _O_NOINHERIT
#define _O_NOINHERIT 0
#endif

typedef unsigned long t4_u32;

/////////////////////////////////////////////////////////////////////////////
// c4_FileStream

c4_FileStream::c4_FileStream (FILE* stream_, bool owned_)
  : _stream (stream_), _owned (owned_)
{
}

c4_FileStream::~c4_FileStream ()
{
  if (_owned)
    fclose(_stream);
}

int c4_FileStream::Read(void* buffer_, int length_)
{
  d4_assert(_stream != 0);

  return (int) fread(buffer_, 1, length_, _stream);
}

bool c4_FileStream::Write(const void* buffer_, int length_)
{
  d4_assert(_stream != 0);

  return (int) fwrite(buffer_, 1, length_, _stream) == length_;
}

/////////////////////////////////////////////////////////////////////////////
// c4_FileStrategy

void c4_FileStrategy::MySeek(TSeek aMode, int pos)
{
	if(aMode != ESeekStart || pos <= 0)
	{
		_rfile.Seek(aMode, pos);
		return;
	}
	
	int size;
	_rfile.Size(size);
	if(pos > size)
	{
		int p = 0;
		_rfile.Seek(ESeekEnd, p);
		TBuf8<1> zero;
		_rfile.Write(size, zero, pos - size);
	}
	else
		_rfile.Seek(aMode, pos);
}

int c4_FileStrategy::MyTell()
{
	int pos = 0;
	_rfile.Seek(ESeekCurrent , pos);
	return pos;
}

c4_FileStrategy::c4_FileStrategy (FILE* file_)
{
	User::LeaveIfError(_fs.Connect());
  ///InitializeIO();
  ResetFileMapping();
}

c4_FileStrategy::~c4_FileStrategy ()
{
  _rfile.Close();
  _fs.Close();
  ResetFileMapping();

  d4_assert(_mapStart == 0);
  ///FinalizeIO();
}

bool c4_FileStrategy::IsValid() const
{ 
  return true;
  ///shine return _file != 0; 
}

t4_i32 c4_FileStrategy::FileSize()
{
  ///shine d4_assert(_file != 0);
	TInt size = 0;
	_rfile.Size(size);
	
  	return size;
}

t4_i32 c4_FileStrategy::FreshGeneration()
{
  d4_assert(false);
  return 0;
}

void c4_FileStrategy::ResetFileMapping()
{
#if q4_WIN32
  if (_mapStart != 0) {
    _mapStart -= _baseOffset;
    d4_dbgdef(BOOL g =)
      ::UnmapViewOfFile((char*) _mapStart);
    d4_assert(g);
    _mapStart = 0;
    _dataSize = 0;
  }

  if (_file != 0) {
    t4_i32 len = FileSize();

    if (len > 0) {
      FlushFileBuffers((HANDLE) _get_osfhandle(_fileno(_file)));
      HANDLE h = ::CreateFileMapping((HANDLE) _get_osfhandle(_fileno(_file)),
                        0, PAGE_READONLY, 0, len, 0);
      d4_assert(h); // check for errors, but can continue without mapping

      if (h) {
        _mapStart = (t4_byte*) ::MapViewOfFile(h, FILE_MAP_READ, 0, 0, len);
        d4_assert(_mapStart != 0);

        if (_mapStart != 0) {
          _mapStart += _baseOffset;
          _dataSize = len - _baseOffset;
        }

        d4_dbgdef(BOOL f =)
        ::CloseHandle(h);
        d4_assert(f);
      }
    }
  }
#elif HAVE_MMAP && !NO_MMAP
  if (_mapStart != 0) {
    _mapStart -= _baseOffset;
    munmap((char*) _mapStart, _baseOffset + _dataSize); // also loses const
    _mapStart = 0;
    _dataSize = 0;
  }

  if (_file != 0) {
    t4_i32 len = FileSize();

    if (len > 0) {
      _mapStart = (const t4_byte*) mmap(0, len, PROT_READ, MAP_SHARED,
                            fileno(_file), 0);
      if (_mapStart != (void*) -1L) {
        _mapStart += _baseOffset;
        _dataSize = len - _baseOffset;
      } else
        _mapStart = 0;
    }
  }
#endif
}

bool c4_FileStrategy::DataOpen(const char* fname_, int mode_)
{
  ///shine d4_assert(!_file);
	TBuf<100> FileName;
	StringToDes(fname_, FileName);
	TBool exist = BaflUtils::FileExists(_fs, FileName);
	
	if(exist)
	{
		TInt aFileMode = mode_ > 0? EFileRead|EFileWrite|EFileShareAny : EFileRead|EFileShareAny;
		User::LeaveIfError(_rfile.Open(_fs, FileName, aFileMode));
	}
	else
	{
		TInt aFileMode = EFileRead|EFileWrite|EFileShareAny;
		User::LeaveIfError(_rfile.Replace(_fs, FileName, aFileMode));
	}
	
    ResetFileMapping();
    return true;

	return false;
}

int c4_FileStrategy::DataRead(t4_i32 pos_, void* buf_, int len_)
{
  d4_assert(_baseOffset + pos_ >= 0);
  ///shine d4_assert(_file != 0);

	int pos = _baseOffset + pos_;
	_rfile.Seek(ESeekStart, pos);
	TPtr8 buf((TUint8*)buf_, len_, len_);
	TInt res = _rfile.Read(buf, len_);
	return (res == KErrNone)? len_ : -1;
/*
 //printf("DataRead at %d len %d\n", pos_, len_);
  return fseek(_file, _baseOffset + pos_, 0) != 0 ? -1 :
    (int) fread(buf_, 1, len_, _file);
*/
}

void c4_FileStrategy::DataWrite(t4_i32 pos_, const void* buf_, int len_)
{
	d4_assert(_baseOffset + pos_ >= 0);
	///shine d4_assert(_file != 0);

	int pos = _baseOffset + pos_;

	MySeek(ESeekStart, pos);
	TPtr8 buf((TUint8*)buf_, len_, len_);
	_rfile.Write(buf, len_);
/*
  if (fseek(_file, pos, 0) != 0 ||
      (int) fwrite(buf_, 1, len_, _file) != len_) {
    _failure = ferror(_file);
    d4_assert(_failure != 0);
    d4_assert(true); // always force an assertion failure in debug mode
    int nn = 0;
  }
*/  
}

void c4_FileStrategy::DataCommit(t4_i32 limit_)
{
  ///shine d4_assert(_file != 0);
  int xxx = MyTell();
	_rfile.Flush();

  if (limit_ > 0) {
    ResetFileMapping(); // remap, since file length may have changed
  }
}

/////////////////////////////////////////////////////////////////////////////
